#ifndef _GAMELAB_OBSERVER_H

#include <list>

/////////////////////////////////////////////////////////////////
//
// Design Pattern: Observer Pattern
//
// Usage:
// class Publisher : public Observable {};
// class Subscriber : public Observer<Pulisher> {};
//
/////////////////////////////////////////////////////////////////

namespace gl {

	class Observable;

	class ObserverBase
	{
		public:
			friend class Observable;

			virtual void __Update() = 0;

		protected:
			Observable* subject_;
	};

	template <typename SubjectType>
		class Observer : public ObserverBase
	{
		public:
			virtual void __Update()
			{
				Update(this->GetSubject());
			};

			virtual void Update(SubjectType* subject) {}

			SubjectType* GetSubject() { return (SubjectType*)subject_; }
	};

	class Observable
	{
		public:
			typedef std::list<ObserverBase*> ObserverCont;

			Observable() : changed_(false) {}
			virtual ~Observable() {}

		public:
			void Attach(ObserverBase* observer)
			{
				if (observer)
				{
					observers_.push_back(observer);
					observer->subject_ = this;
				}
			}

			bool Detach(ObserverBase* observer)
			{
				for (ObserverCont::iterator it = observers_.begin();
						it != observers_.end(); ++ it)
					if (*it == observer)
					{
						observer->subject_ = NULL;
						observers_.erase(it);
						return true;
					}
				return false;
			}

			void Notify()
			{
				if (HasChanged())
				{
					for (ObserverCont::iterator it = observers_.begin();
							it != observers_.end(); ++ it)
						(*it)->__Update();
				}

				SetChanged();
			}

			void SetChanged(bool state = true) { changed_ = state; }
			bool HasChanged() const { return changed_; }

		protected:
			ObserverCont observers_;

			bool changed_;
	};

} // namespace gl

#endif // _GAMELAB_OBSERVER_H

